#ifndef SIMPLE_IO_BYTE_RANGE_HPP
#define SIMPLE_IO_BYTE_RANGE_HPP

#include <cstddef> // std::byte std::nullptr_t
#include <type_traits> // std::enable_if_t std::is_trivially_copyable_v std::is_pointer_v std::is_const_v std::true_type std::false_type
#include <utility> // std::declval

#include "simple/support/range.hpp" // support::range

namespace simple::io
{

	// TODO: move to separate header, maybe even to support library
	using byte_range = support::range<std::byte*>;

	template<typename Type, typename = std::nullptr_t>
	struct has_writable_data : public std::false_type {};
	template<typename Type>
	struct has_writable_data<Type,
	decltype(
		void(std::declval<Type>().data()),
		void(std::declval<Type>().size()),
		void(std::declval<Type>().data() + std::declval<Type>().size()),
		void(*std::declval<Type>().data() = *std::declval<Type>().data()),
		nullptr)>
	: public std::true_type
	{};
	template<typename Type>
	constexpr bool has_writable_data_v = has_writable_data<Type>::value;

	// TODO: require elements to be reasonable like in the overload below
	template <typename T, std::enable_if_t<has_writable_data_v<T>>* = nullptr,
		std::enable_if_t<std::is_pointer_v<decltype(std::declval<T>().data())>>* = nullptr>
	[[nodiscard]]
	byte_range as_byte_range(T& contiguous) noexcept
	{
		auto begin = reinterpret_cast<std::byte*>(contiguous.data());
		auto size = contiguous.size() * sizeof(decltype(*contiguous.data()));
		return {begin, begin + size};
	}

	template <typename T, std::enable_if_t<not has_writable_data_v<T> && std::is_trivially_copyable_v<T> && not std::is_const_v<T> && not std::is_pointer_v<T>>* = nullptr>
	[[nodiscard]]
	byte_range as_byte_range(T& x) noexcept
	{
		auto begin = reinterpret_cast<std::byte*>(&x);
		return {begin, begin + sizeof(x)};
	}

} // namespace simple::io

#endif /* end of include guard */
